package com.ddwl.goods.service.bargain;

import com.alibaba.fastjson.JSON;
import com.ddwl.common.constant.BaseEnum;
import com.ddwl.common.constant.Constant;
import com.ddwl.common.constant.GoodsEnum;
import com.ddwl.common.exception.ParamException;
import com.ddwl.common.feign.api.OrderApi;
import com.ddwl.common.feign.api.UserApi;
import com.ddwl.common.util.BeanConverUtils;
import com.ddwl.common.util.UUIDGenerator;
import com.ddwl.goods.dao.bargain.BargainLogDao;
import com.ddwl.goods.dao.bargain.BargainOrderDao;
import com.ddwl.goods.dao.bargain.BargainSkuDao;
import com.ddwl.goods.model.bargain.BargainActivity;
import com.ddwl.goods.model.bargain.BargainLog;
import com.ddwl.goods.model.bargain.BargainOrder;
import com.ddwl.goods.model.bargain.BargainSku;
import com.ddwl.goods.service.SkuService;
import com.ddwl.goods.util.BargainCommonUtils;
import com.ddwl.schema.bo.goods.bargain.BargainLogBo;
import com.ddwl.schema.bo.goods.bargain.BargainOrderBo;
import com.ddwl.schema.bo.order.BargainActivityOrderBo;
import com.ddwl.schema.util.DateUtil;
import com.ddwl.schema.vo.base.RestVo;
import com.ddwl.schema.vo.goods.SkuVo;
import com.ddwl.schema.vo.goods.bargain.BargainActivityVo;
import com.ddwl.schema.vo.goods.bargain.BargainConfigVo;
import com.ddwl.schema.vo.goods.bargain.BargainLogVo;
import com.ddwl.schema.vo.goods.bargain.BargainOrderSuccessVo;
import com.ddwl.schema.vo.user.UserVo;
import io.gitee.tooleek.lock.spring.boot.annotation.Lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: hemiaojun
 * @Date: 2020/10/31
 * @Description:
 */
@Slf4j
@Service
public class BargainService {

    @Resource
    private BargainActivityService bargainActivityService;
    @Resource
    private BargainConfigService bargainConfigService;
    @Resource
    private BargainOrderDao bargainOrderDao;
    @Resource
    private BargainSkuDao bargainSkuDao;
    @Resource
    private BargainLogDao bargainLogDao;
    @Resource
    private SkuService skuService;
    @Resource
    private UserApi userApi;
    @Resource
    private OrderApi orderApi;

    /**
     * 免费拿
     */
    @Lock(leaseTime = 5)
    @Transactional(rollbackFor = Exception.class)
    public BargainLogVo free(BargainOrderBo bo) {
        BargainActivityVo baVo= bargainActivityService.getById(bo.getActivityId());
        ParamException.isNull(baVo,"该活动不存在");
        ParamException.isTrue(Instant.now().compareTo(baVo.getStartTime()) < 0,"活动还未开始，敬请期待！");
        ParamException.isTrue(Instant.now().compareTo(baVo.getEndTime()) > 0,"对不起，该活动已结束！");
        ParamException.isTrue(baVo.getSurplusStock().compareTo(0) <= 0,"对不起，该商品已售完");
        ParamException.isTrue(GoodsEnum.BargainActivityStatusEnum.FINISH.getCode().equals(baVo.getStatus()),"对不起，该活动已结束！");
        BargainSku bs=bargainSkuDao.selectByBarCode(bo.getBarcode(),bo.getActivityId());
        ParamException.isTrue(bs.getChopStock().compareTo(null==bs.getSales()?0:bs.getSales()) <= 0, "对不起，该商品已售完");

        BargainLogVo blVo = getExistBargainLog(bo, baVo.getLimitQty());
        if (blVo != null) {
            return blVo;
        }
        BargainOrder entity = addBargainOrder(bo, baVo, bs.getId());
        updateSkuStock(bs);
        updateActivity(baVo);
        BargainLogBo blBo = new BargainLogBo();
        blBo.setBargainOrderId(entity.getId());
        blBo.setUserId(bo.getUserId());
        blBo.setNickname(bo.getNickname());
        return helpBargain(blBo,Boolean.FALSE);
    }

    private BargainLogVo getExistBargainLog(BargainOrderBo bo,Integer limitQty) {
        List<BargainOrder> exist=bargainOrderDao.getByActivityIdAndUserId(bo.getActivityId(), bo.getUserId());
        if (!CollectionUtils.isEmpty(exist)) {
            int successNum = 0;
            for (BargainOrder bOrder: exist) {
                if ((GoodsEnum.BargainOrderStatusEnum.PROCESSING.getCode().equals(bOrder.getStatus()) && bOrder.getExpireDate().compareTo(Instant.now()) > 0)
                        || (GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode().equals(bOrder.getStatus()) && bOrder.getPayExpireDate().compareTo(Instant.now()) > 0)) {
                    BargainLog bargainLog = bargainLogDao.getByBargainOrderIdAndUserId(bOrder.getId(), bo.getUserId());
                    if (null != bargainLog) {
                        BargainLogVo blVo = BeanConverUtils.convertBean(bargainLog, BargainLogVo.class);
                        blVo.setChopped(1);
                        blVo.setChopNum(bOrder.getChopNum());
                        blVo.setNeedChopNum(bOrder.getNeedChopNum());
                        blVo.setStatus(bOrder.getStatus());
                        return blVo;
                    }
                }
                if (GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode().equals(bOrder.getStatus())) {
                    successNum++;
                }
            }
            ParamException.isTrue((null != limitQty && limitQty > 0 && successNum >= limitQty), "该商品每人限购"+limitQty+"件！");
        }
        return null;
    }

    private void updateActivity(BargainActivityVo baVo) {
        BargainActivity ba = new BargainActivity();
        int stock = baVo.getSurplusStock()-1;
        ba.setId(baVo.getId());
        ba.setSurplusStock(stock);
        if (stock <= 0) {
            ba.setStatus(GoodsEnum.BargainActivityStatusEnum.FINISH.getCode());
        }
        bargainActivityService.updateActivity(ba);
    }

    private void updateSkuStock(BargainSku bs) {
        BargainSku bSku = new BargainSku();
        bSku.setId(bs.getId());
        Integer sales = null == bs.getSales() ? 0 : bs.getSales();
        bSku.setSales(sales+1);
        Integer stock = bs.getChopStock();
        if (null == stock || 0 == stock) {
            bSku.setChopStock(0);
        }else{
            bSku.setChopStock(stock - 1);
        }
        bargainSkuDao.updateByPrimaryKeySelective(bSku);
    }

    private BargainOrder addBargainOrder(BargainOrderBo bo, BargainActivityVo baVo, String bargainSkuId) {
        BargainOrder entity = BeanConverUtils.convertBean(baVo, BargainOrder.class,"id");
        entity.setActivityId(bo.getActivityId());
        entity.setBargainSkuId(bargainSkuId);
        entity.setBarcode(bo.getBarcode());
        entity.setUserId(bo.getUserId());
        entity.setNickname(bo.getNickname());
        entity.setChoppedPrice(BigDecimal.ZERO);
        entity.setDeliveryType(bo.getDeliveryType());
        entity.setReserveMobile(bo.getReserveMobile());
        entity.setAddrId(bo.getAddrId());
        entity.setChopNum(0);
        entity.setExpireDate(DateUtil.addHours(Instant.now(), baVo.getExpireHour()));
        entity.setStatus(GoodsEnum.BargainOrderStatusEnum.PROCESSING.getCode());
        entity.setNeedChopNum(baVo.getChopNum());
        bargainOrderDao.insert(entity);
        return entity;
    }

    @Lock(leaseTime = 5)
    @Transactional(rollbackFor = Exception.class)
    public BargainLogVo helpBargain(BargainLogBo bo,Boolean isNewUser) {
        BargainOrder boVo=bargainOrderDao.selectByPrimaryKey(bo.getBargainOrderId());
        log.info("[method helpBargain BargainOrder:{} ]", JSON.toJSONString(boVo));
        validCheck(boVo);
        BargainActivityVo baVo = bargainActivityService.getById(boVo.getActivityId());
        ParamException.isNull(baVo,"该活动不存在");
        log.info("[method helpBargain BargainActivityVo:{} ]", JSON.toJSONString(baVo));
        BargainLogVo exist = getBargainLogVo(bo);
        if (null != exist){
            return exist;
        }
        BargainConfigVo bcVo=bargainConfigService.getBargainConfig(baVo.getShopId());
        if (!bo.getUserId().equals(boVo.getUserId())) {
            BargainLogVo limit=checkBargainTimes(bo, bcVo);
            if(null!=limit){
                return limit;
            }
        }
        //商品价格
        BigDecimal itemPrice=boVo.getItemPrice();
        //底价
        BigDecimal thresholdPrice = null == boVo.getThresholdPrice() ? BigDecimal.ZERO : boVo.getThresholdPrice();
        //已经砍掉的价格
        BigDecimal choppedPrice = null == boVo.getChoppedPrice() ? BigDecimal.ZERO : boVo.getChoppedPrice();
        BigDecimal thresholdRandomMin = null == baVo.getThresholdRandomMin() ? BigDecimal.ZERO : baVo.getThresholdRandomMin();
        BigDecimal thresholdRandomMax = null == baVo.getThresholdRandomMax() ? BigDecimal.ZERO : baVo.getThresholdRandomMax();

        BargainLog entity = BeanConverUtils.convertBean(bo, BargainLog.class);
        entity.setId(UUIDGenerator.getShortUUID());
        Integer newUserRate = null == baVo.getNewUserRate() ? 0 : baVo.getNewUserRate();
        Boolean newUser = isNewUser(bo.getUserId(), isNewUser, baVo.getNewUserMode(), newUserRate);
        log.info("[newUser and newUserRate : {},{}]",newUser,newUserRate);
        fillBargainLogInfo(bo.getUserId(), entity,newUser);

        BigDecimal firstMoney = null == baVo.getFirstMoney() ? BigDecimal.ZERO : baVo.getFirstMoney();
        BigDecimal surplusPrice= itemPrice.subtract(thresholdPrice).subtract(choppedPrice);
        if (boVo.getChopNum() == 0 && firstMoney.compareTo(BigDecimal.ZERO) > 0) {
            firstMoney = firstMoney.compareTo(surplusPrice) > 0 ? surplusPrice : firstMoney;
            entity.setChoppedMoney(firstMoney);
        }else{
            BigDecimal prepareReducePrice = BigDecimal.ZERO;
            Integer rate = 0;
            if(newUser){
                rate = newUserRate;
            }
            Integer hardRate = 1;
            if (null != bcVo) {
                hardRate = null == bcVo.getHardRate() ? new Integer(1) : bcVo.getHardRate();
            }
            Boolean hardMode = isHardMode(bcVo,hardRate);
            if(GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode().equals(boVo.getStatus())){
                surplusPrice= itemPrice.subtract(choppedPrice);
                prepareReducePrice=BargainCommonUtils.randomReducePrice(surplusPrice,thresholdRandomMin,thresholdRandomMax,rate);
                if(hardMode){
                    prepareReducePrice=prepareReducePrice.divide(BigDecimal.valueOf(hardRate), 2, BigDecimal.ROUND_UP);
                    prepareReducePrice = prepareReducePrice.compareTo(new BigDecimal("0.01")) < 0 ? new BigDecimal("0.01") : prepareReducePrice;
                }
            }else{
                if(GoodsEnum.BargainChopTypeEnum.RANDOM.getCode().equals(baVo.getChopType())){
                    prepareReducePrice=BargainCommonUtils.randomReducePrice(surplusPrice,baVo.getRandomMin(),baVo.getRandomMax(),rate);
                    if(hardMode){
                        prepareReducePrice=prepareReducePrice.divide(BigDecimal.valueOf(hardRate), 2, BigDecimal.ROUND_UP);
                        prepareReducePrice = prepareReducePrice.compareTo(new BigDecimal("0.01")) < 0 ? new BigDecimal("0.01") : prepareReducePrice;
                    }
                }
                if (GoodsEnum.BargainChopTypeEnum.FIXED.getCode().equals(baVo.getChopType())) {
                    Integer totalChopNum=baVo.getChopNum();
                    Integer willReducedTimes=1;
                    if(hardMode){
                        totalChopNum = totalChopNum * hardRate;
                    }
                    if(newUser){
                        willReducedTimes = newUserRate;
                    }
                    prepareReducePrice=BargainCommonUtils.getReducePrice(surplusPrice,totalChopNum,boVo.getChopNum(),willReducedTimes,0);
                }
            }
            log.info("[prepareReducePrice : {}]",prepareReducePrice);
            entity.setChoppedMoney(prepareReducePrice);
        }
        bargainLogDao.insert(entity);

        BigDecimal totalChoppedPrice=choppedPrice.add(entity.getChoppedMoney());
        updateBargainOrder(boVo, newUserRate,newUser, itemPrice, thresholdPrice, thresholdRandomMin, thresholdRandomMax, totalChoppedPrice);
        if(boVo.getItemPrice().compareTo(totalChoppedPrice)<=0){
            ((BargainService) AopContext.currentProxy()).payZeroOrder(bo.getProduct(), boVo,totalChoppedPrice);
            updateSaleInfo(baVo,BigDecimal.ZERO);
        }
        BargainLogVo blVo=BeanConverUtils.convertBean(entity,BargainLogVo.class);
        blVo.setNeedChopNum(boVo.getNeedChopNum());
        return blVo;
    }

    private void fillBargainLogInfo(String userId, BargainLog entity, Boolean newUser) {
        UserVo vo = userApi.getProfile(userId).getResult();
        if (null != vo) {
            entity.setUserAvatar(vo.getAvatar());
            entity.setNickname(vo.getNickname());
        }
        if (newUser) {
            entity.setNewUser(1);
        } else {
            entity.setNewUser(0);
        }
    }

    private Boolean isNewUser(String userId , Boolean isNewUser, Integer newUserMode, Integer newUserRate) {
        if(isNewUser){
            return Boolean.TRUE;
        }
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(newUserMode) && newUserRate.compareTo(0) > 0) {
            List<BargainLog> logList = bargainLogDao.getByUserId(userId);
            RestVo<Boolean> rest = userApi.firstLogin(userId);
            if (CollectionUtils.isEmpty(logList) && rest.getResult()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    private Boolean isHardMode(BargainConfigVo bcVo,Integer hardRate) {
        Boolean hardMode = Boolean.FALSE;
        if (null != bcVo) {
            BigDecimal hardModeMoney = null == bcVo.getHardModeMoney() ? BigDecimal.ZERO : bcVo.getHardModeMoney();
            Integer hardModeOrder = null == bcVo.getHardModeOrder() ? 0 : bcVo.getHardModeOrder();
            if(hardRate.compareTo(new Integer(1))>0){
                if (hardModeMoney.compareTo(BigDecimal.ZERO) > 0 || hardModeOrder.compareTo(0) > 0) {
                    BargainOrderSuccessVo successVo = bargainOrderDao.getTodaySuccess();
                    log.info("[BargainOrderSuccessVo : {}]", JSON.toJSONString(successVo));
                    BigDecimal successAmount = null == successVo.getSuccessAmount() ? BigDecimal.ZERO : successVo.getSuccessAmount();
                    if (successAmount.compareTo(hardModeMoney) >= 0 || successVo.getSuccessOrder().compareTo(hardModeOrder) >= 0) {
                        hardMode = Boolean.TRUE;
                    }
                }
            }
        }
        return hardMode;
    }

    private BargainLogVo checkBargainTimes(BargainLogBo bo,BargainConfigVo bcVo) {
        if (null != bcVo) {
            Integer limitTimes = null == bcVo.getLimitTimes() ? 0 : bcVo.getLimitTimes();
            if (limitTimes.compareTo(0) > 0) {
                Instant startTime;
                Instant endTime = Instant.now();
                Integer logNum = 0;
                if (bcVo.getLimitPeriod().equals(GoodsEnum.BargainLimitPeriodEnum.DAY.getCode())) {
                    startTime = DateUtil.getTimesMorning();
                    logNum = bargainLogDao.countWithinPeriod(bo.getUserId(), startTime, endTime);
                }
                if (bcVo.getLimitPeriod().equals(GoodsEnum.BargainLimitPeriodEnum.WEEK.getCode())) {
                    startTime = DateUtil.getMondayOfThisWeek();
                    logNum = bargainLogDao.countWithinPeriod(bo.getUserId(), startTime, endTime);
                }
                if(logNum.compareTo(limitTimes) > 0){
                    BargainLogVo limit = new BargainLogVo();
                    limit.setChopped(2);
                    return limit;
                }
            }
        }
        return null;
    }

    private void validCheck(BargainOrder boVo) {
        ParamException.isNull(boVo,"该用户的订单不存在");
        ParamException.isTrue(GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode().equals(boVo.getStatus()),"该订单已完成");
        ParamException.isTrue(GoodsEnum.ActivityPlatformEnum.MERCHANT.getCode().equals(boVo.getPlatform()) && GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode().equals(boVo.getStatus()),"该订单已经助力到底价，不能再助力了哦");
        ParamException.isTrue(Instant.now().compareTo(boVo.getExpireDate()) > 0,"该订单已失效");
        ParamException.isTrue(GoodsEnum.BargainOrderStatusEnum.FAILURE.getCode().equals(boVo.getStatus()),"该订单已失效");
        //商品价格
        BigDecimal itemPrice=boVo.getItemPrice();
        //已经砍掉的价格
        BigDecimal choppedPrice = null == boVo.getChoppedPrice() ? BigDecimal.ZERO : boVo.getChoppedPrice();
        ParamException.isTrue((choppedPrice.compareTo(itemPrice) >= 0), "该订单已完成");
    }

    public String buy(String bargainOrderId, String userId,String product) {
        BargainOrder entity=bargainOrderDao.selectByPrimaryKey(bargainOrderId);
        ParamException.isNull(entity,"该订单不存在");
        ParamException.isTrue(GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode().equals(entity.getStatus()),"该订单已支付成功，请勿重复操作");
        ParamException.isFalse(GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode().equals(entity.getStatus()),"该订单未砍价成功，不能购买");
        ParamException.isFalse(entity.getUserId().equals(userId),"该订单不属于你，不能购买");
        String result = payOrder(product, entity,entity.getChoppedPrice());
        updateBargainOrderInfo(entity);
        BargainActivityVo bargainActivityVo = bargainActivityService.getById(entity.getActivityId());
        if (null != bargainActivityVo) {
            BigDecimal price = entity.getItemPrice().subtract(entity.getChoppedPrice());
            updateSaleInfo(bargainActivityVo,price);
        }
        return result;
    }

    private void updateBargainOrderInfo(BargainOrder entity) {
        BargainOrder update = new BargainOrder();
        update.setId(entity.getId());
        update.setStatus(GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode());
        update.setCompleteDate(Instant.now());
        bargainOrderDao.updateByPrimaryKeySelective(update);
    }

    private void updateSaleInfo(BargainActivityVo bargainActivityVo,BigDecimal price) {
        BargainActivity ba = new BargainActivity();
        int orders = null == bargainActivityVo.getOrders() ? 0 : bargainActivityVo.getOrders();
        ba.setId(bargainActivityVo.getId());
        ba.setOrders(orders+1);
        BigDecimal alreadyAmount = null == ba.getAmount() ? BigDecimal.ZERO : ba.getAmount();
        ba.setAmount(alreadyAmount.add(price));
        bargainActivityService.updateActivity(ba);
    }

    @Async
    public void payZeroOrder(String product, BargainOrder entity,BigDecimal totalChoppedPrice) {
        createOrderInfo(product, entity, totalChoppedPrice);
    }

    public String payOrder(String product, BargainOrder entity,BigDecimal totalChoppedPrice) {
        return createOrderInfo(product, entity, totalChoppedPrice);
    }

    public String createOrderInfo(String product, BargainOrder entity, BigDecimal totalChoppedPrice) {
        SkuVo skuVo = skuService.getByBarCode(entity.getBarcode());
        BargainActivityOrderBo baoBo = new BargainActivityOrderBo();
        baoBo.setUserId(entity.getUserId());
        baoBo.setUserName(entity.getNickname());
        baoBo.setUserAdderId(entity.getAddrId());
        baoBo.setProduct(product);
        if (entity.getItemPrice().compareTo(totalChoppedPrice) <= 0) {
            baoBo.setZeroOrder(1);
        } else {
            baoBo.setZeroOrder(0);
        }
        List<BargainActivityOrderBo.SkuList> list = new ArrayList();
        BargainActivityOrderBo.SkuList sku = new BargainActivityOrderBo.SkuList();
        sku.setActivityId(entity.getId());
        sku.setItemId(entity.getItemId());
        sku.setSkuId(skuVo.getId());
        sku.setSkuQty(1);
        sku.setSkuDesc(skuVo.getItemSpecs());
        sku.setUserAdderId(entity.getAddrId());
        sku.setDeliveryType(entity.getDeliveryType());
        sku.setUserMobile(entity.getReserveMobile());
        list.add(sku);
        baoBo.setSkuLists(list);
        RestVo<String> restVo = orderApi.createBargainOrder(baoBo);
        return restVo.getResult();
    }

    private BargainLogVo getBargainLogVo(BargainLogBo bo) {
        BargainLog bargainLog=bargainLogDao.getByBargainOrderIdAndUserId(bo.getBargainOrderId(), bo.getUserId());
        if (null != bargainLog) {
            BargainLogVo exist=BeanConverUtils.convertBean(bargainLog,BargainLogVo.class);
            exist.setChopped(1);
            return exist;
        }
        return null;
    }

    private void updateBargainOrder(BargainOrder boVo, Integer newUserRate,Boolean newUser, BigDecimal itemPrice, BigDecimal thresholdPrice, BigDecimal thresholdRandomMin, BigDecimal thresholdRandomMax, BigDecimal totalChoppedPrice) {
        BargainOrder bargainOrder = new BargainOrder();
        bargainOrder.setId(boVo.getId());
        if(newUser){
            bargainOrder.setChopNum(boVo.getChopNum()+newUserRate);
        }else{
            bargainOrder.setChopNum(boVo.getChopNum()+1);
        }
        if(totalChoppedPrice.compareTo(itemPrice)>=0){
            bargainOrder.setStatus(GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode());
            bargainOrder.setCompleteDate(Instant.now());
        } else if (totalChoppedPrice.compareTo(itemPrice.subtract(thresholdPrice)) >= 0) {
            bargainOrder.setStatus(GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode());
            bargainOrder.setPayExpireDate(DateUtil.addHours(boVo.getExpireDate(), 1));
        }
        bargainOrder.setChoppedPrice(totalChoppedPrice);
        bargainOrderDao.updateByPrimaryKeySelective(bargainOrder);
    }

    public Boolean check(String bargainOrderId) {
        BargainOrder boVo=bargainOrderDao.selectByPrimaryKey(bargainOrderId);
        ParamException.isNull(boVo,"该用户的订单不存在");
        if (GoodsEnum.ActivityPlatformEnum.MERCHANT.getCode().equals(boVo.getPlatform())) {
            if (GoodsEnum.BargainOrderStatusEnum.THRESHOLD_BUY.getCode().equals(boVo.getStatus())) {
                return Boolean.FALSE;
            }
        }
        if (GoodsEnum.BargainOrderStatusEnum.SUCCESS.getCode().equals(boVo.getStatus()) || Instant.now().compareTo(boVo.getExpireDate()) > 0 || GoodsEnum.BargainOrderStatusEnum.FAILURE.getCode().equals(boVo.getStatus())) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}
